/**
 *
 * Code for Calculate IP and UDP Checksum
 *
 * source: tcpdump project print-ip.c and print-udp.c
 *
 */

u_short in_cksum(const u_short *addr, register u_int len, int csum)
{
    int nleft = len;
    const u_short *w = addr;
    u_short answer;
    int sum = csum;

    /*
     *  Our algorithm is simple, using a 32 bit accumulator (sum),
     *  we add sequential 16 bit words to it, and at the end, fold
     *  back all the carry bits from the top 16 bits into the lower
     *  16 bits.
     */
    while (nleft > 1)
    {
        sum += *w++;
        nleft -= 2;
    }
    if (nleft == 1)
        sum += htons(*(u_char *) w << 8);

    /*
     * add back carry outs from top 16 bits to low 16 bits
     */
    sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
    sum += (sum >> 16); /* add carry */
    answer = ~sum; /* truncate to 16 bits */
    return (answer);
}

int udp_cksum(register const struct ip *ip, register const struct udphdr *up, register u_int len)
{
    union phu
    {
        struct phdr
        {
            u_int32_t src;
            u_int32_t dst;
            u_char mbz;
            u_char proto;
            u_int16_t len;
        } ph;
        u_int16_t pa[6];
    } phu;
    register const u_int16_t *sp;

    /* pseudo-header.. */
    phu.ph.len = htons((u_int16_t) len);
    phu.ph.mbz = 0;
    phu.ph.proto = IPPROTO_UDP;
    memcpy(&phu.ph.src, &ip->ip_src.s_addr, sizeof(u_int32_t));
    if (IP_HL(ip) == 5)
        memcpy(&phu.ph.dst, &ip->ip_dst.s_addr, sizeof(u_int32_t));
    else
        phu.ph.dst = ip->ip_dst.s_addr;

    sp = &phu.pa[0];
    return in_cksum((u_short *) up, len, sp[0] + sp[1] + sp[2] + sp[3] + sp[4] + sp[5]);
}

